package com.system.core.sms;

import java.util.Iterator;
import java.util.Random;
import java.util.Set;

import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest;
import com.system.core.sms.dto.config.SmsCodeConfig;
import com.system.core.sms.dto.sendcode.SendCodeReq;
import com.system.core.sms.dto.sendcode.UserCode;
import com.system.core.sms.dto.sendcode.VerifyCodeReq;
import com.system.core.sms.dto.smstemplate.SmsCodeReq;

import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
@Validated
public class SmsCodeComponent {
	
	
	private SmsCodeConfig smsCodeConfig;

	private RedisTemplate<String, Object> redisTemplate;
	private AliyunSmsService aliyunSmsService;
	private String acceptedChars = "0123456789";
	
	
	private final String prefix="sms_code:";
	private int acceptCharLength = acceptedChars.length();

	private Random random = new Random();
	public SmsCodeComponent(SmsCodeConfig smsCodeConfig, RedisTemplate<String, Object> redisTemplate,
			AliyunSmsService aliyunSmsService) {
		this.smsCodeConfig = smsCodeConfig;
		this.redisTemplate = redisTemplate;
		this.aliyunSmsService = aliyunSmsService;
	}
	
	public String getWord() {
        return getWord(null);
    }

	public String getWord(Integer codeLen) {
        String bufferRand = "";
        if (smsCodeConfig.getUseDefaultCode()) {
            bufferRand = smsCodeConfig.getDefaultCode();
        } else {
        	if(codeLen==null || codeLen==0)
        		codeLen = smsCodeConfig.getCodeLength();
            for (int i = 0; i < codeLen; i++) {
            	bufferRand += String.valueOf(acceptedChars.charAt(random.nextInt(acceptCharLength)));
            }
        }
        log.info("[短信码]生成短信码：{}",bufferRand);
        return bufferRand;
    }
	
	public String getWord(Integer codeLen,boolean defaultstr) {
        String bufferRand = "";
        if (defaultstr) {
            bufferRand = smsCodeConfig.getDefaultCode();
        } else {
        	if(codeLen==null || codeLen==0)
        		codeLen = smsCodeConfig.getCodeLength();
            for (int i = 0; i < codeLen; i++) {
            	bufferRand += String.valueOf(acceptedChars.charAt(random.nextInt(acceptCharLength)));
            }
        }
        log.info("[短信码]生成短信码：{}",bufferRand);
        return bufferRand;
    }
	
	public void sendCode(@Valid SendCodeReq sendCodeReq) throws Exception{
		String redisKey = prefix+sendCodeReq.getPhone();
		BoundSetOperations<String, Object> userCodeSet = redisTemplate.boundSetOps(redisKey);
		Set<Object> allItem = userCodeSet.members();
		UserCode userCode1 = new UserCode();
		userCode1.setBizTypeName(sendCodeReq.getBizTypeName());
		boolean contained = allItem.contains(userCode1);
		String smsCode = getWord();
		if(!contained) {
			firstSend(sendCodeReq, redisKey, userCodeSet, smsCode);
		} else {
			continueSend(sendCodeReq,redisKey, userCodeSet, allItem, smsCode);
		}
	}

	public boolean verifyCode(@Valid VerifyCodeReq serifyCodeReq) throws Exception{
		String redisKey = prefix+serifyCodeReq.getPhone();
		BoundSetOperations<String, Object> userCodeSet = redisTemplate.boundSetOps(redisKey);
		Set<Object> allItem = userCodeSet.members();
		UserCode userCode1 = new UserCode();
		userCode1.setBizTypeName(serifyCodeReq.getBizTypeName());
		boolean contained = allItem.contains(userCode1);
		Long now = System.currentTimeMillis();
		if(contained) {
			Iterator<Object> iter;
			for(iter=allItem.iterator();iter.hasNext();) {
				UserCode tmp = (UserCode)iter.next();
				/**
				 * 1、15分钟内有效
				 */
				if(tmp.getBizTypeName() == serifyCodeReq.getBizTypeName() 
						&&tmp.getSmsCode().equalsIgnoreCase(serifyCodeReq.getSmsCode())
						&&!tmp.isUsed()
						&&((now-tmp.getLastUpdateTimeMillis())/1000/60)<15) {
					userCodeSet.remove(tmp);
					boolean isEmpty = false;
					if(userCodeSet.size()==0) {
						log.warn("该Key={}值为空，重新设置超时时间！",redisKey);
						isEmpty = true;
					}
					tmp.setUsed(true);
					userCodeSet.add(tmp);
					if(isEmpty) {
						redisTemplate.expire(redisKey, smsCodeConfig.getExpire());
					}
					return true;
				}
			}
		} else {
			throw new Exception("请先发送验证码！");
		}
		return false;
	}
	
	private void continueSend(SendCodeReq sendCodeReq,String redisKey, BoundSetOperations<String, Object> userCodeSet,
			Set<Object> allItem, String smsCode) throws Exception {
		Iterator<Object> iter;
		for(iter=allItem.iterator();iter.hasNext();) {
			Object obj = iter.next();
			UserCode tmp = (UserCode)obj;
			if(tmp.getBizTypeName() == sendCodeReq.getBizTypeName()) {
				if(tmp.getSendTimes()>=smsCodeConfig.getTimesPeerDay())
					throw new Exception(tmp.getBizTypeName().getMessage()+"操作，短信发送次数超过限制每日"+smsCodeConfig.getTimesPeerDay()+"次");
				long now = System.currentTimeMillis();
				long intervalTimeMillis = now - tmp.getLastUpdateTimeMillis();
				if(intervalTimeMillis/1000<=60)
					throw new Exception(tmp.getBizTypeName().getMessage()+"操作，短信发送请间隔1分钟");
				// 如果Code没有使用过，重发还是使用之前的Code
				if(!tmp.isUsed())
					smsCode = tmp.getSmsCode();
				userCodeSet.remove(tmp);
				boolean isEmpty = false;
				if(userCodeSet.size()==0) {
					log.warn("该Key={}值为空，重新设置超时时间！",redisKey);
					isEmpty = true;
				}
				tmp.setSendTimes(tmp.getSendTimes()+1);
				tmp.setLastUpdateTimeMillis(System.currentTimeMillis());
				tmp.setSmsCode(smsCode);
				tmp.setUsed(false);
				userCodeSet.add(tmp);
				if(isEmpty) {
					redisTemplate.expire(redisKey, smsCodeConfig.getExpire());
				}
				sendMsg(sendCodeReq, smsCode);
				log.debug("[短信码]用户{}第{}次使用{}业务发送短信验证码{}",
						sendCodeReq.getPhone(),
						tmp.getSendTimes(),
						sendCodeReq.getBizTypeName().getMessage(),
						smsCode);
			}
		}
	}

	private void firstSend(SendCodeReq sendCodeReq, String redisKey, BoundSetOperations<String, Object> userCodeSet,
			String smsCode) throws Exception {
		UserCode userCode = new UserCode();
		userCode.setBizTypeName(sendCodeReq.getBizTypeName());
		userCode.setLastUpdateTimeMillis(System.currentTimeMillis());
		userCode.setSmsCode(smsCode);
		userCodeSet.add(userCode);
		if(redisTemplate.hasKey(redisKey)) {
			redisTemplate.expire(redisKey, smsCodeConfig.getExpire());
		} else {
			throw new Exception("不包含："+redisKey);
		}
		sendMsg(sendCodeReq, smsCode);
		log.debug("[短信码]用户{}第一次使用{}业务发送短信验证码{},失效时间是：{}-{}",
				sendCodeReq.getPhone(),
				sendCodeReq.getBizTypeName().getMessage(),
				smsCode,
				smsCodeConfig.getExpire(),
				redisKey);
	}
	
	private void sendMsg(SendCodeReq sendCodeReq, String smsCode) throws Exception {
		SmsCodeReq smsCodeReq = SmsCodeReq.builder().code(smsCode).build();
		SendSmsRequest sendSmsRequest = SendSmsRequest.builder()
				.signName(smsCodeConfig.getSignName())
				.templateCode(smsCodeConfig.getSmsCodeTemplateName())
				.phoneNumbers(sendCodeReq.getPhone())
				.templateParam(smsCodeReq.toString())
				.build();
		var sendSmsResponseBody = aliyunSmsService.send(sendSmsRequest);
		log.debug("[短信]发送结果：{}",sendSmsResponseBody.toMap());
		if(!sendSmsResponseBody.getCode().equalsIgnoreCase("ok")) {
			throw new Exception("[阿里云]"+sendSmsResponseBody.getMessage());
		}
	}
}
